//下面再看另一个例子。

const a1 = [undefined, undefined, undefined];
const a2 = [, , , ];

a1.length // 3
a2.length // 3

a1[0] // undefined
a2[0] // undefined

a1[0] === a2[0] // true

/*
上面代码中，数组a1的成员是三个undefined，数组a2的成员是三个空位。
这两个数组很相似，长度都是 3，每个位置的成员读取出来都是undefined。

但是，它们实际上存在重大差异。
*/

0 in a1 // true
0 in a2 // false

a1.hasOwnProperty(0) // true
a2.hasOwnProperty(0) // false

Object.keys(a1) // ["0", "1", "2"]
Object.keys(a2) // []

a1.map(n => 1) // [1, 1, 1]
a2.map(n => 1) // [, , ,]

/*
上面代码一共列出了四种运算，数组a1和a2的结果都不一样。
前三种运算（in运算符、数组的hasOwnProperty方法、Object.keys方法）都说明，数组a2取不到属性名。
最后一种运算（数组的map方法）说明，数组a2没有发生遍历。
*/

/*
为什么a1与a2成员的行为不一致？数组的成员是undefined或空位，到底有什么不同？

规格的12.2.5 小节《数组的初始化》给出了答案。
*/
数组成员可以省略。
只要逗号前面没有任何表达式， 数组的length属性就会加 1， 并且相应增加其后成员的位置索引。
被省略的成员不会被定义。
如果被省略的成员是数组最后一个成员， 则不会导致数组length属性增加。

/*
上面的规格说得很清楚，数组的空位会反映在length属性，也就是说空位有自己的位置，但是这个位置的值是未定义，
即这个值是不存在的。
如果一定要读取，结果就是undefined（因为undefined在 JavaScript 语言中表示不存在）。


这就解释了为什么in运算符、数组的hasOwnProperty方法、Object.keys方法，都取不到空位的属性名。
因为这个属性名根本就不存在，规格里面没说要为空位分配属性名(位置索引），只说要为下一个元素的位置索引加 1。

至于为什么数组的map方法会跳过空位，请看下一节。
*/
